(function(window, undefined) {

// UA触屏标识
var ua = navigator.userAgent.toLowerCase();
	window.isTouch = ua.indexOf("android") >= 0 || ua.indexOf("iphone") >= 0 || ua.indexOf("ipad") >= 0;

// 补充方法
var whiteSpaces = /^\s+|\s+$/g;
!String.prototype.trim && (String.prototype.trim = function() { return this.replace(whiteSpaces, ""); });
	
// 防止重复加载
if (window.$) { return; }
var $ = window.$ = function(selector, context, length) {
	var result;
	if ( !isNaN(context) ) {
		length = context;
		context = undefined;
	}

	if (null == selector) {
		result = [ ];
	} else if ('string' === typeof selector) {
		result = (context || document).querySelectorAll(selector);
	} else if (selector instanceof Nodes) {
		return selector;
	} else if ( selector.nodeType != null || isWindow(selector) ) {
		result = [selector];
	} else {
		result = toArray(selector);
	}

	return new Nodes(result, length);
};


// #region 工具方法
var toString = Object.prototype.toString;

function isFunction(value) { return toString.call(value) === '[object Function]'; }

function isArray(value) { return toString.call(value) === "[object Array]"; }

// 把有length属性且可以通过数字索引的对象转换为数组
function toArray(arr) {
	var copy;
	try {
		copy = Array.prototype.slice.call(arr);
	} catch (e) {
		copy = [ ];
		var i = col.length;
		while (i) {
			copy[--i] = arr[i];
		}
	}
	return copy;
}

function extend(target) {
	for (var i = 1, p, src; i < arguments.length; i++) {
		src = arguments[i];
		if (src) {
			for (p in src) {
				target[p] = src[p];
			}
		}
	}

	return target;
}

function each(obj, callback) {
	if (obj != null) {
		var i = -1, len = obj.length;
		if ( len === undefined || isFunction(obj) ) {
			for (i in obj) {
				if ( false === callback.call(obj[i], obj[i], i, obj) ) {
					break;
				}
			}
		} else {
			while (++i < len) {
				if ( false === callback.call(obj[i], obj[i], i, obj) ) {
					break;
				}
			}
		}
	}
	
	return obj;
}

function access(elems, key, value, isExec, fns) {
	var len = elems.length, i, temp;
	
	if (typeof key === 'object') {
		for (var k in key) {
			access(elems, k, key[k], isExec, fns);
		}
		return elems;
	}

	if (value !== undefined) {
		isExec = isExec && isFunction(value);

		i = -1;
		while (++i < len) {
			fns.set.call( elems, elems[i], key,
				isExec ? value.call( elems[i], i ) : value );
		}
	
		return elems;
	}

	return len ? fns.get.call(elems, elems[0], key) : undefined;
}

function first(obj) {
    if (obj != null) {
		if ( obj.length === undefined || isFunction(obj) ) {
			for (i in obj) {
			    if ( obj.hasOwnProperty(i) ) {
				    return {
				        key : i,
				        value : obj[i]
				    };
				}
			}
		} else {
			return {
			    key : 0,
			    value : obj[0]
			};
		}
	}
}

var tplCache = { };
function parseTpl(tpl, values, isCached) {
	if (null == tpl) { return; }
	if (null == values) { return tpl; }

	var fn = tplCache[tpl];
	if (!fn) {
		var t = tpl.replace(/[\r\t\n]/g, " ");
		console.log(1, t);
		
		t = t.replace(/'(?=[^#]*#>)/g, "\t");
		console.log(2, t);
		
		t = t.split("'").join("\\'");
		console.log(3, t);
		
		t = t.split("\t").join("'");
		console.log(4, t);
		
		t = t.replace(/<#=(.+?)#>/g, "',$1,'");
		console.log(5, t);
		
		t = t.split("<#").join("');");
		console.log(6, t);
		
		t = "var _=[];with(obj){_.push('" + t + "');}return _.join('');";
		console.log(7, t);
		
		fn = new Function("obj", "var _=[];with(obj){_.push('" +
				tpl.replace(/[\r\t\n]/g, " ")
				.replace(/'(?=[^#]*#>)/g, "\t")
				.split("'").join("\\'")
				.split("\t").join("'")
				.replace(/<#=(.+?)#>/g, "',$1,'")
				.split("<#").join("');")
				.split("#>").join("_.push('")
				+ "');}return _.join('');");
		isCached !== false && (tplCache[tpl] = fn);
	}
	
	return fn(values);
}

function isEmptyObject(obj) {
    for (var i in obj) {
        return false;
    }
    return true;
}

function isWindow(obj) { return obj.alert && obj.document; };

extend($, {
	extend : extend,
	each : each,
	access : access,
	parseTpl : parseTpl,
	isArray : isArray,
	isFunction : isFunction,
	first : first,
	isEmptyObject : isEmptyObject
});
// #endregion


// #region 样式操作
var FLOAT_NAME = 'cssFloat',
	STYLE_NOTIN_PXS = /^(?:zIndex|fontWeight|opacity|zoom|)$/,
	DASH = /-([a-z])/gi,
	MULTI_SPACE = /\s+/g;

function getCurrentStyle(el, name) {
	return el.ownerDocument.defaultView.getComputedStyle(el, null)[name];
}
// 修复样式名
function fixStyleName(name) {
	return 'float' === name.toLowerCase() ? FLOAT_NAME : name.replace(DASH, function($0, $1) {
		return $1.toUpperCase();
	});
}
// 修复样式值
function fixStyleValue(name, val) {
	return STYLE_NOTIN_PXS.test(name) || '' === val || isNaN(val) ? val : val + 'px';
}
// 解析类名
function explainClassName(className) {
	var temp;
	if ('string' === typeof className) {
		temp = className.replace(MULTI_SPACE, ' ');
		className = className.split(' ');
	} else {
		temp = className.join(' ');
	}
	className.raw = temp;

	return className;
}
// 获取元素布局大小
function getSize(elem, which, extra) {
	if (!elem) { return; }

	which = which.replace(/^[a-z]/, function($0) { return $0.toUpperCase(); });		// 首字母大写
	extra = extra || '';
	
	var oldDisplay = elem.style.display;
	elem.style.display = 'block';
	
	var size = elem['offset' + which],
		borders = 'Width' == which ? ['Left', 'Right'] : ['Top', 'Bottom'];
   
	for (var i = 0, b; b = borders[i]; i++) {
		if ( -1 == extra.indexOf('padding') ) {
			size -= parseFloat( $.style.getCss(elem, 'padding' + b) ) || 0;
		}
		if ( -1 == extra.indexOf('border') ) {
			size -= parseFloat( $.style.getCss(elem, 'border' + b + 'Width') ) || 0;
		}
		if ( extra.indexOf('margin') != -1 ) {
			size += parseFloat( $.style.getCss(elem, 'margin' + b) ) || 0;
		}
	}
	
	elem.style.display = oldDisplay;
	
	return size;
}

$.style = {
	getCss : function(el, key) {
		key = fixStyleName(key);
		if (el.style[key] !== undefined) {
			return el.style[key] !== '' ? el.style[key] : getCurrentStyle(el, key);
		}
	},
	setCss : function(el, key, val) {
		key = fixStyleName(key);
		val = fixStyleValue(key, val);
		var curVal = el.style[key];
		if (curVal !== undefined && curVal != val) {
			el.style[key] = val;
		}
	},
	hasClass : function(el, classes) {
	    if (el.className != '') {
	        var className = ' ' + el.className + ' ', i = classes.length;
	        while (--i >= 0) {
	            if ( className.indexOf(' ' + classes[i] + ' ') == -1 ) {
	                return false;
	            }
	        }
	        return true;
	    } else {
	        return false;
	    }
	},
	addClass : function(el, classes) {
		if ('' === el.className && classes.raw) {
			el.className = classes.raw;
		} else {
			var className = ' ' + el.className + ' ', i = -1, len = classes.length;
			while (++i < len) {
				if ( -1 === className.indexOf(' ' + classes[i] + ' ') ) {
					className += (classes[i] + ' ');
				}
			}
			el.className = className.trim();
		}
	},
	removeClass : function(el, classes) {
		if (classes) {
			switch (el.className) {
				case classes.raw:
					el.className = '';
				break;

				case '':
					return;
				break;
				
				default:
					var className = ' ' + el.className + ' ', i = -1, len = classes.length;
					while (++i < len) {
						className = className.replace(' ' + classes[i] + ' ', ' ');
					}
					el.className = className.trim();
				break;
			}
		} else {
			el.className = '';
		}
	}
};
// #endregion


// #region 事件操作
var eventId = 0;
// 去除事件
function disposeEvent(eventName, handler, data) {
}

$.event = {
	// 事件容器名
	nameSpace : "Events",
	
	addEvent : function(el, eventNames, handler, data){
		each(eventNames.split(","), function(name){
			var trueHandler = $.event.delegate(el, name, handler, data);
			el.addEventListener(name, trueHandler, false);
		});
	},
	
	removeEvent: function(el, eventNames){
		each(eventNames.split(","), function(name){
			var trueHandler = $.event.unDelegate(el, name, handler);
			el.removeEventListener(name, trueHandler, false);
		});
	},
	
	delegate : function(elem, eventName, handler, data) {
		var $e = $.event, events = elem[$e.nameSpace] = elem[$e.nameSpace] || {}, 	// 取得事件Hash表引用
			id = handler["eventID"] = handler["eventID"] || ++eventId; 	// 获取不重复的事件编号
		// 生成特定事件Hash表
		events[eventName] = events[eventName] || {};

		var trueHandler = events[eventName][id];
		if (!trueHandler) {
			trueHandler = function(e) {
				e.data = data;
				// 把上下文绑定回元素
				var result = handler.call(elem, e);
				false === result && e.preventDefault();
				true === e.cancelBubble && e.stopPropagation();
				return result;
			};
			events[eventName][id] = trueHandler;
		}

		return trueHandler;
	},
	
	unDelegate : function(elem, eventName, handler) {
		var $e = $.event, eventSpace = elem[t.eventSpace];
		if (eventSpace) {
			var eventId = handler["eventID"];
			if (eventId) {
				trueHandler = eventSpace[eventName][eventId];
				eventSpace[eventName][eventId] = null;
				return trueHandler;
			}
		}
	}
};
// #endregion


// #region DOM操作
var attrMap = {
	'class' : 'className',
	'for' : 'htmlFor'
};	// HTML元素特殊属性名映射

$.dom = {
	getAttr : function(el, key) { return el[attrMap[key] || key]; },
	setAttr : function(el, key, val) { el[attrMap[key] || key] = val; }
};
// #endregion


// #region 节点类
function Nodes(result, length) {
	for (var i = result.length - 1; i >= 0; i--) {
		this[i] = result[i];
	}
	this.length = result.length;
}
$.Nodes = Nodes;
extend(Nodes.prototype, {
	each : function(callback) { return each(this, callback); },

	attr : function(key, value) {
		return access(this, key, value, true, {
			get : $.dom.getAttr,
			set : $.dom.setAttr
		});
	},

	parent : function() {
	    return this[0] ? this[0].parentNode : undefined;
	},
	
	trim : function(){
		return this.each(function(node){
			$.each(node.childNodes, function(childNode){
				childNode.nodeType == 3 && node.removeChild(childNode);
			});
		});
	},
	
	/* remove : function(from, to) {
		var rest = this.slice((to || from) + 1 || this.length);
		this.length = from < 0 ? this.length + from : from;
		return this.push.apply(this, rest);
	}, */

	html : function(value) {
		return this.attr("innerHTML", value);
	},
	
	val : function(value) {
	    return this.attr("value", value);
	},

	on : function(eventNames, handler, data) {
		return this.each(function(el) {
			$.event.addEvent(el, eventNames, handler, data);
		});
	},

	detach : function(eventNames, handler) {
		return this.each(function(el) {
			$.event.removeEvent(el, eventNames, handler);
		});
	},

	css : function(key, val) {
		return access(this, key, val, true, {
			get : $.style.getCss,
			set : $.style.setCss
		});
	},

	addClass : function(className) {
		className = explainClassName(className);
		if (className.length > 0) {
			this.each(function(el) {
				$.style.addClass(el, className);
			});
		}
		return this;
	},
	
	hasClass : function(className) {
	    if (className != null && className !== "") {
			className = explainClassName(className);
		}
		var flag = true;
		
		this.each(function(el) {
			flag = flag && $.style.hasClass(el, className);
			return flag;
		});

		return flag;
	},

	removeClass : function(className) {
		if (className != null && className !== "") {
			className = explainClassName(className);
		}
		this.each(function(el) {
			$.style.removeClass(el, className);
		});

		return this;
	},

	width : function(extra) { return getSize(this[0], 'width', extra); },
	
	height : function(extra) { return getSize(this[0], 'height', extra); },
	
	remove : function() {
	    var el = this[0];
	    if (el) {
	        var parent = el.parentNode;
	        parent && parent.removeChild(el);
	    }
	},
	
	push : Array.prototype.push,
	
	indexOf : Array.prototype.indexOf,
	
	unshift : Array.prototype.unshift,
	
	shift : Array.prototype.shift
});
// #endregion


// #region 动态加载
var encodeURIComponent = window.encodeURIComponent, setTimeout = window.setTimeout;

// 转换为URL参数
function toQueryString(values) {
	if (values) {
		var str = [];
		for (var n in values) {
			values[n] != null && str.push(n + "=" + encodeURIComponent(values[n]));
		}
		return str.join("&").replace(/%20/g, "+");
	}
}

$.ajax = {
	send : function(options) {
		var xhr = new XMLHttpRequest(), hasCompleted,
			method = options.method ? options.method.toUpperCase() : "GET";
		if(typeof xhr == 'undefined')	alert("No XMLHttpRequest");
		
		options.async = null == options.async ? true : Boolean(options.async);
		
		options.data = options.data || { };
		
		// 连接参数键值对
		var postData, url = options.url;
		if (options.data) {
			postData = toQueryString(options.data);
			if ("GET" === method && postData) {
				url += (url.indexOf("?") != -1 ? "&" : "?") + postData;
				postData = undefined;
			}
		}
		
		// 添加随机数
		if (options.useRndNum !== false) {
		    url += (url.indexOf("?") != -1 ? "&" : "?") + "rnd=" + Math.random();
		}
		
		var stateChange = function() {
			if (4 == xhr.readyState) {
				hasCompleted = true;
				options.onLoad && options.onLoad.call(this, xhr);
				var eventName = 200 == xhr.status ? "onSuccess" : "onError";
				options[eventName] && options[eventName].call(this, xhr);
				options.onComplete && options.onComplete.call(this, xhr);
			}
		};

		if (options.async) {
			options.timeout > 0 && setTimeout(function() {
				if (!hasCompleted) {
					xhr.abort();
					options.onLoad && options.onLoad.call(this, xhr);
					options.onTimeout && options.onTimeout(xhr);
					options.onComplete && options.onComplete.call(this, xhr);
				}
			}, options.timeout);

			xhr.onreadystatechange = stateChange;
		}

		xhr.open(method, url, options.async, options.username, options.password);
		
		xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
		if ("POST" === method) {
			xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		}

        if (options.headers) {
            $.each(options.headers, function(val, key) {
                xhr.setRequestHeader(key, val);
            });
        }
		
		xhr.send(postData);

		!options.async && stateChange();

		return xhr;
	},

	getJSON : function(options) {
		var _onSuccess = options.onSuccess;
		if (_onSuccess) {
			options.onSuccess = function(xhr) {
				var data;
				try {
					data = JSON.parse(xhr.responseText);
				} catch (e) {
					console.log("Error in getJSON:" + xhr.responseText);
				}

				_onSuccess.call(this, data);
			};
		}
		return this.send(options);
	}
};
// #endregion


})(window);